package com.atguigu.gmall.config;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.model.GmallCorrelationData;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;

//  将这个配置类放入spring 容器中.
@Component
public class MQProducerAckConfig implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnCallback {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    //  当这个类加的时候，先执行init方法。加载数据.
    @PostConstruct
    public void init(){
        rabbitTemplate.setReturnCallback(this);
        rabbitTemplate.setConfirmCallback(this);
    }
    /**
     * 判断这个消息是否到交换机
     * @param correlationData 封装了一个id，还有消息对象.
     * @param ack 判断是否到交换机
     * @param text 显示的文本内容.
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String text) {
        if (ack){
            System.out.println("到交换机");
        }else {
            System.out.println("没到交换机");
            //  调用重发方法.
            this.retrySendMsg(correlationData);
        }

    }

    /**
     * 判断这个消息是否到队列. 如果这个消息没有到队列，则这个方法执行! 到队列，则不执行！
     * @param message   消息主体
     * @param replayCode    响应码
     * @param replayText    响应内容
     * @param exchange      交换机
     * @param routingKey    路由键
     */
    @Override
    public void returnedMessage(Message message, int replayCode, String replayText, String exchange, String routingKey) {
        System.out.println(message);
        System.out.println("replayCode:\t"+replayCode);
        System.out.println("replayText:\t"+replayText);
        System.out.println("exchange:\t"+exchange);
        System.out.println("routingKey:\t"+routingKey);


        //  说明消息没有到队列; 通过存储的uuid 去缓存中获取数据.
        String uuid = (String) message.getMessageProperties().getHeaders().get("spring_returned_message_correlation");
        //  从缓存中获取数据.
        String gmallJson = (String) redisTemplate.opsForValue().get(uuid);
        //  强制转换
        GmallCorrelationData gmallCorrelationData = JSON.parseObject(gmallJson, GmallCorrelationData.class);
        //  如果是延迟消息。不走重试！
        if (gmallCorrelationData.isDelay()) return;

        //  调用重发方法.
        this.retrySendMsg(gmallCorrelationData);

    }

    /**
     * 重试发送方法.
     * @param correlationData
     */
    private void retrySendMsg(CorrelationData correlationData) {
        //  转为子类
        GmallCorrelationData gmallCorrelationData = (GmallCorrelationData) correlationData;
        //  获取到重试次数
        int retryCount = gmallCorrelationData.getRetryCount();
        if (retryCount>=3){
            System.out.println("超过次数"); //  将这个消息落盘到数据库表.
        } else {
            //  修改重试次数
            retryCount++;
            //  将重试次数写回缓存
            gmallCorrelationData.setRetryCount(retryCount);
            redisTemplate.opsForValue().set(gmallCorrelationData.getId(), JSON.toJSONString(gmallCorrelationData),10, TimeUnit.MINUTES);
            //  判断是否发送延迟消息
            if (gmallCorrelationData.isDelay()){
                //  延迟消息.
                this.rabbitTemplate.convertAndSend(gmallCorrelationData.getExchange(),gmallCorrelationData.getRoutingKey(),gmallCorrelationData.getMessage(), message -> {
                    message.getMessageProperties().setDelay(gmallCorrelationData.getDelayTime()*1000);
                    return message;
                },gmallCorrelationData);
            } else {
                //  发送普通消息：
                rabbitTemplate.convertAndSend(gmallCorrelationData.getExchange(),gmallCorrelationData.getRoutingKey(),gmallCorrelationData.getMessage()
                        ,gmallCorrelationData);

            }

        }
    }

}
